<?php

namespace App\Models\Panx;

use App\Observers\BosCleanObserver;
use App\Services\Utils\Bosable;
use App\Services\Locale\Language;
use App\Services\Utils\Memorable;
use Illuminate\Database\Eloquent\Builder;
use Illuminate\Database\Eloquent\Collection;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Relations\Pivot;

/**
 * Class Category
 *
 * @property int id
 * @property int level
 * @property int priority
 * @property string name
 * @property string const - deprecated 原来用于对应英文名称，由多语言表代替
 * @property string icon
 * @property string icon_active - deprecated same as icon
 *
 * @property-read Product[]|Collection products
 * @property-read Pivot|CategoryPivotCustomer pivot
 * @property-read Collection|CategoryDescription[] categoryDescriptions
 *
 * @property-write null|\Illuminate\Support\Collection brands - 虚拟仓产品不同分类下的品牌
 * @property-write null|\Illuminate\Support\Collection customer_brands
 *
 * @method static self filterCustomerPivotIsShow(int $is_show = null)
 *
 * @package App\Models\Panx
 * @mixin Builder
 */
class Category extends Model
{
    use Bosable;
    use Memorable;

    const TOP_CATEGORY = 0;        // parent_id =0 是商品的一级分类

    const LEVEL_FIRST = 1;         // level = 1 是商品一级分类
    const LEVEL_THIRD = 3;         // level = 3 是商品的三级分类

    const BLUSHER_ID = 1; // 腮红
    const EYE_SHADOW_ID = 2; // 眼影
    const EYEBROW_PENCIL_ID = 3; // 眉笔
    const EYELASH_ID = 4; // 睫毛
    const LIPSTICK_ID = 5; // 口红
    const GLASS_ID = 10;

    const DEFAULT_CATEGORY_IDS = [
        self::BLUSHER_ID,
        self::EYE_SHADOW_ID,
        self::EYEBROW_PENCIL_ID,
        self::EYELASH_ID,
        self::LIPSTICK_ID,
    ];

    // 可以上传素材
    const HAS_ASSET_IDS = [
        self::BLUSHER_ID,
        self::EYE_SHADOW_ID,
        self::EYEBROW_PENCIL_ID,
        self::EYELASH_ID,
        self::LIPSTICK_ID,
    ];

    // 自定义的类别
    const CUSTOM_TYPE_COSMETICS = self::HAS_ASSET; // 化妆品
    const CUSTOM_TYPE_SKINCARE= self::NO_ASSET;    // 护肤品

    const CUSTOM_TYPE_MAPS = [
        Language::ZH_CN => [
            self::CUSTOM_TYPE_COSMETICS => '化妆品',
            self::CUSTOM_TYPE_SKINCARE  => '护肤品',
        ],
        Language::ZH_HK => [
            self::CUSTOM_TYPE_COSMETICS => '化妝品',
            self::CUSTOM_TYPE_SKINCARE  => '護膚品',
        ],
        Language::EN    => [
            self::CUSTOM_TYPE_COSMETICS => 'cosmetics',
            self::CUSTOM_TYPE_SKINCARE  => 'skincare',
        ],
    ];

    public $bos_columns = ['icon', 'icon_active'];

    protected $category_bos_key_prefix = 'category/icon/';

    /**
     * @var Collection|self[]
     */
    protected static $all_cache;

    protected $table = 'categories';

    protected $guarded = [];

    protected $casts = [
        'id'        => 'int',
        'parent_id' => 'int',
        'has_asset' => 'int',
        'level'     => 'int',
        'sort'      => 'int',
    ];

    /**
     * 是否关联素材
     */
    const HAS_ASSET = 1;
    const NO_ASSET = 0;

    /**
     * @return Category|Model|null|object
     */
    public static function getBasicCare() :?Category
    {
       return (new self)->where('name','基础护理')->first();
    }

    /**
     * @used-by $icon
     *
     * @param $icon
     * @throws \Exception
     */
    public function setIconAttribute($icon)
    {
        if (is_file($icon)) {
            $this->attributes['icon'] = $this->uploadPostFile($icon, $this->category_bos_key_prefix);
        }
    }

    /**
     * @used-by $icon
     *
     * @return null|string
     */
    public function getIconAttribute()
    {
        if (! $this->attributes['icon']) return null;

        return $this->getStaticUrl($this->attributes['icon']);
    }

    /**
     * @deprecated - 高亮图标已不需要再显示
     * @used-by $icon_active
     *
     * @param $icon_active
     * @throws \Exception
     */
    public function setIconActiveAttribute($icon_active)
    {
        if (is_file($icon_active)) {
            $this->attributes['icon_active'] = $this->uploadPostFile($icon_active, $this->category_bos_key_prefix);
        }
    }

    /**
     * @deprecated - 高亮图标已不需要再显示，防止前端使用该字段，直接返回 icon
     * @used-by $icon_active
     *
     * @return null|string
     */
    public function getIconActiveAttribute()
    {
        return $this->icon;

//        if (! $this->attributes['icon_active']) return null;
//        return $this->getStaticUrl($this->attributes['icon_active']);
    }

    /**
     * @deprecated - 已新增多语言表，不再使用 const 表示英文
     *
     * 如果不在中文模式下只返回 const 英文
     *
     * @return string
     */
    public function getNameAttribute()
    {
        return $this->attributes['name'];
    }

    /**
     * 有素材是化妆品 / 没素材是护肤品
     *
     * @param int|null $category_id
     *
     * @return bool
     */
    public static function hasAsset(?int $category_id): bool
    {
        return in_array($category_id, self::hasAssetIds());
    }

    /**
     * id是化妆品
     *
     * @param int $category_id
     *
     * @return bool
     */
    public static function isCosmetic(int $category_id): bool
    {
        return self::hasAsset($category_id);
    }

    /**
     * id是护肤品
     *
     * @param int $category_id
     *
     * @return bool
     */
    public static function isSkinCare(int $category_id): bool
    {
        return ! self::hasAsset($category_id);
    }

    /**
     * @param int $has_asset
     *
     * @return array
     */
    public static function hasAssetIds(int $has_asset = self::HAS_ASSET): array
    {
        return (new self)
            ->where('has_asset', $has_asset)
            ->pluck('id')
            ->all();
    }

    /**
     * @param int $has_asset
     *
     * @return array
     */
    public static function hasAssetIdsCache(int $has_asset = self::HAS_ASSET): array
    {
        return self::singletonMemorize(function () use ($has_asset) { // 跑测试如果在同 Test 文件中先使用，再新增会导致缓存中没有新增的 id
            return (new self)
                ->where('has_asset', $has_asset)
                ->pluck('id')
                ->all();
        })();
    }

    /**
     * @param int $category_id
     * @return string|null
     */
    public static function mapIDtoName(int $category_id): ?string
    {
        self::$all_cache = self::$all_cache ?? self::all();

        return self::singletonMemorize(function () use ($category_id) {
            /** @var Category $self */
            $self = self::$all_cache->find($category_id);

            return $self->name ?? null;
        })();
    }

    /**
     * @return \Illuminate\Database\Eloquent\Relations\HasMany
     */
    public function products()
    {
        return $this->hasMany(Product::class, 'category_id', 'id');
    }

    public function productMods()
    {
        return $this->hasMany(ProductMod::class, 'category_id', 'id');
    }

    /**
     * @return \Illuminate\Database\Eloquent\Relations\HasMany
     */
    public function childrenCategories()
    {
        return $this->hasMany(Category::class, 'parent_id', 'id');
    }

    /**
     * 递归调用
     * @return Builder
     */
    public function allChildrenCategories()
    {
        return $this->childrenCategories()->latest('priority')
            ->with('allChildrenCategories');
    }

    /**
     * @deprecated - 命名不规范 parentCategory 就可以了，不需要 get
     *
     * @return \Illuminate\Database\Eloquent\Relations\BelongsTo
     */
    public function parentCategory()
    {
        return $this->belongsTo(Category::class, 'parent_id', 'id');
    }

    /**
     * @deprecated - 命名不规范 checkIsMoveToChild 检查能移动子分类（父分类ID）？，理解不了
     *
     *  检测是否可以更新到这个父类下
     *
     * @param $parent_id
     * @return mixed
     */
    public function checkIsMoveToChild($parent_id)
    {
        $child_parent_id = $this->id;

        $children = Category::where('parent_id', $child_parent_id)
            ->with('getChildCategory')
            ->get();

        return $this->checkChild($children, $parent_id);
    }

    /**
     * @deprecated - 命名不规范， checkChild 是什么，含意不明
     *
     * @param $children
     * @param $parent_id
     * @return mixed
     */
    private function checkChild($children, $parent_id)
    {
        return $children->contains(function ($child) use ($parent_id) {
                    if ($child->id === (int)$parent_id) { return true; }

                    if (count($child->getChildCategory))  return $this->checkChild($child->getChildCategory, $parent_id);

                    return false;
              });
    }

    /**
     * @return bool
     */
    public function hasChildrenAndHasGoods(): bool
    {
        if ($this->childrenCategories()->first()) return true;

        if ($this->products()->first()) return true;

//        if ($this->productMods()->first()) return true;

        return false;
    }

    /**
     *  检测是否是第三级类别
     *
     * @param $parent_id
     * @return bool
     */
    public  function checkIsMoveToThreeChild($parent_id)
    {
        $three_category = self::find($parent_id);

        $two_category = $three_category->getParentCategory;

        if ($two_category && ($two_category->getParentCategory)) {
            return false;
        } else {
            return true;
        }
    }

    /**
     * 是否为PANX初始化的五类商品
     *
     * @return bool
     */
    public function isInitialCategory(): bool
    {
        return in_array($this->id, self::DEFAULT_CATEGORY_IDS);
    }

    /**
     * 是否为有customer可以操作此产品类别
     *
     * @return bool
     */
    public function hasCustomerOperateCategory(): bool
    {
        return (bool)CategoryPivotCustomer::where('category_id', $this->id)->first();
    }

    /**
     * 导出常量映射文本
     *
     * @example namespace App\Models\Panx; Category::dumpConstMap();
     */
    public static function dumpConstMap()
    {
        $dump_file = storage_path('cate_types.txt');
        $const_column = 'const';
        $remark_column = 'name';

        $const_fmt = "const %s = %s; // %s " . PHP_EOL;
        $map_wrap_fmt = 'const REMARK_MAP = [' . PHP_EOL . '%s' . '];' . PHP_EOL;
        $map_fmt = "\t self::%s => '%s', " . PHP_EOL;

        $const_str = '';
        $map_str = '';
        self::all()->map(
            function (self $self) use (
                $const_column, $remark_column, $const_fmt, $map_fmt, &$const_str, &$map_str
            ) {
                $const_str .= sprintf($const_fmt, $self->{$const_column}, $self->id, $self->{$remark_column});
                $map_str .= sprintf($map_fmt, $self->{$const_column}, $self->{$remark_column});
            }
        );

        $map_str = sprintf($map_wrap_fmt, $map_str) ;

        file_put_contents($dump_file, $const_str . PHP_EOL . $map_str );

        return true;
    }

    /**
     * 注册删除 bosKey 事件绑定
     */
    protected static function boot()
    {
        parent::boot();

        /*
         * 监听bos清理事件
         */
        self::observe(BosCleanObserver::class);
    }

    /**
     * @deprecated
     * 返回对应语言当前应显示的 name 的 column
     *
     * @return string
     */
    public static function getLocalNameColumn(): string
    {
        return Language::appIsCN() ? 'name' : 'const';
    }

    /**
     * @used-by filterCustomerPivotIsShow - 过滤商家是否设置显示对应的分类
     *
     * @param Builder $query
     * @param null|int $is_show
     */
    public function scopeFilterCustomerPivotIsShow(Builder $query, int $is_show = null): void
    {
        /**
         * @uses Builder::callScope()
         */
        if (isset($is_show)) {
            $query->where((new CategoryPivotCustomer)->qualifyColumn('is_show'), $is_show);
        }
    }

    /**
     * 根据自定义的类型排序 - 有无关联素材代表 化妆品 / 护肤品
     *
     * @param Collection $categories
     * @return Collection
     */
    public static function groupCategoryByCustomType(Collection $categories)
    {
        $language = app()->getLocale();

        $group_categories = $categories->groupBy('has_asset');

        $group_categories->transform(function(Collection $group_category, int $has_asset) use($language){
            return [
                'custom_type_name' => self::CUSTOM_TYPE_MAPS[$language][$has_asset],
                'custom_type_id'   => $has_asset,
                'categories'       => $group_category,
            ];
        });

        return $group_categories->values();
    }

    /**
     * @return \Illuminate\Database\Eloquent\Relations\HasMany
     */
    public function categoryDescriptions()
    {
        return $this->hasMany(CategoryDescription::class, 'category_id', 'id');
    }

}
